/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "pc5/ltevdsmp.h"
#include <arpa/inet.h>
#include <iostream>
#include <glog/logging.h>

namespace air {
namespace net {

LtevDsmp::LtevDsmp() {}

LtevDsmp::~LtevDsmp() {}

/**
 * @brief 将DSMP数据进行解析，进一步得到应用数据
 * @param dsmpData[in]dsmp的数据
 * @param size[in]dsmp数据的字节个数
 * @param out[out]如果返回值为true此值有效,此值记录了解析结果
 * @return true
 * @return false
 */
bool LtevDsmp::Decode(const std::string& str_in, uint16_t* aid,
                      std::string* str_out) const {
  if (!aid || !str_out) {
    LOG(WARNING) << "aid or out null";
    return false;
  }
  const char* dsmpData = str_in.data();
  size_t size = str_in.size();

  size_t offset = 0;
  uint8_t flag = 0;

  if (dsmpData == nullptr || size <= 5) {
    LOG(WARNING) << "dmspData==nullptr or size<=5";
    return false;
  }
  if (use_adapta_layer_) {
    // 验证协议是否使用的DSMP
    if (*(dsmpData + offset) != protocol_type_) {
      LOG(WARNING) << "dmspData PROTOCOLTYPE!=" << protocol_type_;
      return false;
    }
    offset += 1;
  }
  flag = *(dsmpData + offset);
  offset += 1;
  // 验证DSMP版本
  if ((flag >> 5) != dsmp_version_ && (flag >> 5) != 1) {
    LOG(WARNING) << "dmspData DSMPVERSION error:" << dsmp_version_ << " "
                 << (flag >> 5);
    return false;
  }
  // 验证是否有DSMP扩展
  if ((flag >> 4) & 0x01) {
    uint8_t flag = *(dsmpData + offset);
    uint16_t extendNum = 0;
    if (!(flag & 0x80)) {  // 一个字节
      extendNum = *(dsmpData + offset);
      offset += 1;
    } else if ((flag >> 6) == 0x02) {  // 两个字节
      extendNum = *(dsmpData + offset) << 8;
      extendNum |= (uint8_t) * (dsmpData + offset + 1);
      offset += 2;
    } else {
      return false;
    }
    for (int i = 0; i < extendNum; i++) {
      offset += 1;
      flag = *(dsmpData + offset);
      uint16_t contentLen = 0;
      if (!(flag & 0x80)) {  // 一个字节
        contentLen = *(dsmpData + offset);
        offset += 1;
      } else if ((flag >> 6) == 0x02) {  // 两个字节
        contentLen = (*(dsmpData + offset) & 0x3f) << 8;
        contentLen |= (uint8_t) * (dsmpData + offset + 1);
        offset += 2;
      } else {
        return false;
      }
      offset += contentLen;
    }
  }
  // 验证AID
  uint16_t tempAid = 0;
  flag = *(dsmpData + offset);
  if (!(flag & 0x80)) {  // 一个字节
    tempAid = *(dsmpData + offset);
    offset += 1;
  } else if ((flag >> 6) == 0x02) {  // 两个字节
    tempAid =
        ((*(dsmpData + offset) << 8) | (uint8_t) * (dsmpData + offset + 1)) -
        0x7F80;
    if (tempAid <= 127) {
      return false;
    }
    offset += 2;
  } else {
    return false;
  }
  *aid = tempAid;
  uint16_t dataLen =
      ntohs(*reinterpret_cast<uint16_t*>(const_cast<char*>(dsmpData + offset)));
  offset += 2;
  // 验证数据长度是否匹配
  if (size != offset + dataLen) {
    LOG(WARNING) << "dmspData size!=len";
    return false;
  }

  str_out->assign(dsmpData + offset, dataLen);

  return true;
}

bool LtevDsmp::Encode(const std::string& str_in, uint16_t aid,
                      std::string* str_out) const {
  const char* data = str_in.data();
  size_t size = str_in.size();

  if (data == nullptr || size == 0 || !str_out) {
    LOG(WARNING) << "data==nullptr or size==0";
    return false;
  }
  if (use_adapta_layer_) {
    // 拼装使用的协议字节
    str_out->append(1, protocol_type_);
  }
  uint8_t flag = 0;
  flag |= (dsmp_version_ << 5);
  // 拼装DSMP版本和扩展域标志
  str_out->append(1, flag);
  // 拼装AID
  if (aid <= 0x7f) {
    str_out->append(1, aid);
  } else if (aid <= 0x3fff) {
    str_out->append(1, (aid + 0x7F80) >> 8);
    str_out->append(1, (aid + 0x7F80) & 0xFF);
  } else {
    LOG(WARNING) << "aid invalid";
    str_out->clear();
    return false;
  }
  // 拼装数据长度,大端
  str_out->append(1, size >> 8);
  str_out->append(1, size);
  // 拼装数据
  str_out->append(data, size);
  return true;
}

}  // namespace net
}  // namespace air
